/*
 * $RCSfile: DefaultHandler.java,v $$
 * $Revision: 1.1 $
 * $Date: 2013-4-24 $
 *
 * Copyright (C) 2008 Skin, Inc. All rights reserved.
 *
 * This software is the proprietary information of Skin, Inc.
 * Use is subject to license terms.
 */
package com.skin.webcat.exchange;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.skin.webcat.database.Column;
import com.skin.webcat.exchange.transform.DataTransform;
import com.skin.webcat.exchange.transform.DateTransform;
import com.skin.webcat.exchange.transform.NumberTransform;
import com.skin.webcat.exchange.transform.StringTransform;

/**
 * <p>Title: DefaultHandler</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2006</p>
 * @author xuesong.net
 * @version 1.0
 */
public class DefaultHandler implements ProcessHandler {
    private String tableName;
    private Connection connection;
    private PreparedStatement statement;
    private List<DataTransform> transforms;
    private static final Timestamp DEFAULT_TIMESTAMP = new Timestamp(0);
    private static final Logger logger = LoggerFactory.getLogger(DefaultHandler.class);

    /**
     * default
     */
    public DefaultHandler() {
    }

    /**
     * @param connection
     */
    public DefaultHandler(Connection connection) {
        this.connection = connection;
    }

    /**
     * @param columns
     * @param dataSet
     * @throws SQLException
     */
    @Override
    public void addBatch(List<Column> columns, DataSet dataSet) throws SQLException {
        int i = 0;
        int size = columns.size();
        String columnName = null;
        String javaTypeName = null;
        Column column = null;

        if(logger.isInfoEnabled()) {
            logger.info(this.getInsertSql(columns, dataSet));
        }

        if(this.statement == null) {
            this.statement = this.connection.prepareStatement(this.getPreparedSql(columns));
        }
        
        for(i = 0; i < size; i++) {
            column = columns.get(i);
            columnName = column.getColumnName();
            javaTypeName = column.getJavaTypeName();

            if(javaTypeName.equalsIgnoreCase("boolean") || javaTypeName.equalsIgnoreCase("Boolean")) {
                if(column.getNullable()) {
                    this.statement.setBoolean(i + 1, dataSet.getBoolean(columnName, false));
                }
                else {
                    this.statement.setBoolean(i + 1, dataSet.getBoolean(columnName));
                }
            }
            else if(javaTypeName.equalsIgnoreCase("byte") || javaTypeName.equalsIgnoreCase("Byte")) {
                if(column.getNullable()) {
                    this.statement.setByte(i + 1, dataSet.getByte(columnName, Byte.valueOf((byte)0)));
                }
                else {
                    this.statement.setByte(i + 1, dataSet.getByte(columnName));
                }
            }
            else if(javaTypeName.equalsIgnoreCase("short") || javaTypeName.equalsIgnoreCase("Short")) {
                if(column.getNullable()) {
                    this.statement.setShort(i + 1, dataSet.getShort(columnName, Short.valueOf((short)0)));
                }
                else {
                    this.statement.setShort(i + 1, dataSet.getShort(columnName));
                }
            }
            else if(javaTypeName.equalsIgnoreCase("int") || javaTypeName.equalsIgnoreCase("Integer")) {
                if(column.getNullable()) {
                    this.statement.setInt(i + 1, dataSet.getInteger(columnName, Integer.valueOf(0)));
                }
                else {
                    this.statement.setInt(i + 1, dataSet.getInteger(columnName));
                }
            }
            else if(javaTypeName.equalsIgnoreCase("float") || javaTypeName.equalsIgnoreCase("Float")) {
                if(column.getNullable()) {
                    this.statement.setFloat(i + 1, dataSet.getFloat(columnName, Float.valueOf(0.0f)));
                }
                else {
                    this.statement.setFloat(i + 1, dataSet.getFloat(columnName));
                }
            }
            else if(javaTypeName.equalsIgnoreCase("double") || javaTypeName.equalsIgnoreCase("Double")) {
                if(column.getNullable()) {
                    this.statement.setDouble(i + 1, dataSet.getDouble(columnName, Double.valueOf(0.0d)));
                }
                else {
                    this.statement.setDouble(i + 1, dataSet.getDouble(columnName));
                }
            }
            else if(javaTypeName.equalsIgnoreCase("long") || javaTypeName.equalsIgnoreCase("Long")) {
                if(column.getNullable()) {
                    this.statement.setLong(i + 1, dataSet.getLong(columnName, Long.valueOf(0L)));
                }
                else {
                    this.statement.setLong(i + 1, dataSet.getLong(columnName));
                }
            }
            else if(javaTypeName.equalsIgnoreCase("Date")
                    || javaTypeName.equalsIgnoreCase("Timestamp")
                    || javaTypeName.equalsIgnoreCase("java.util.Date")
                    || javaTypeName.equalsIgnoreCase("java.sql.Date") || javaTypeName.equalsIgnoreCase("java.sql.Timestamp")) {
                if(column.getNullable()) {
                    this.statement.setTimestamp(i + 1, dataSet.getTimestamp(columnName, DEFAULT_TIMESTAMP));
                }
                else {
                    this.statement.setTimestamp(i + 1, dataSet.getTimestamp(columnName));
                }
            }
            else if(javaTypeName.equalsIgnoreCase("char") || javaTypeName.equalsIgnoreCase("String")) {
                if(column.getNullable()) {
                    this.statement.setString(i + 1, dataSet.getString(columnName, ""));
                }
                else {
                    this.statement.setString(i + 1, dataSet.getString(columnName));
                }
            }
            else {
                this.statement.setObject(i + 1, dataSet.getValue(columnName));
            }
        }

        this.statement.addBatch();
    }

    /**
     * @return int[]
     * @throws SQLException
     */
    public int[] executeBatch() throws SQLException {
        if(this.statement != null) {
            if(logger.isDebugEnabled()) {
                long t1 = System.currentTimeMillis();
                int[] rows = this.statement.executeBatch();
                long t2 = System.currentTimeMillis();
                logger.debug("executeBatch.times: " + (t2 - t1));
                return rows;
            }
            return this.statement.executeBatch();
        }

        return new int[0];
    }

    /**
     * @param sql
     * @return int
     * @throws SQLException
     */
    public int delete(String sql) throws SQLException {
        Statement statement = null;

        try {
            statement = this.connection.createStatement();
            return statement.executeUpdate(sql);
        }
        finally {
            this.close(statement);
        }
    }

    /**
     * @param columns
     * @param dataSet
     * @return String
     * @throws SQLException
     */
    public String getInsertSql(List<Column> columns, DataSet dataSet) throws SQLException {
        if(this.transforms == null) {
            this.transforms = this.getTransforms(columns);
        }

        StringBuilder buffer = new StringBuilder();
        buffer.append("INSERT INTO ");
        buffer.append(this.getTableName());
        buffer.append("(");

        int i = 0;
        int size = columns.size();
        Column column = null;
        DataTransform transform = null;

        for(i = 0; i < size; i++) {
            column = columns.get(i);
            buffer.append(column.getColumnName());
            
            if(i < (size - 1)) {
                buffer.append(", ");
            }
            else {
                buffer.append(") values (");
            }
        }

        for(i = 0; i < size; i++) {
            column = columns.get(i);
            transform = this.transforms.get(i);
            buffer.append(transform.toString(dataSet.getValue(column.getColumnName())));

            if(i < (size - 1)) {
                buffer.append(", ");
            }
            else {
                buffer.append(");");
            }
        }
        return buffer.toString();
    }

    /**
     * @param columns
     * @return List<DataTransform>
     */
    public List<DataTransform> getTransforms(List<Column> columns) {
        List<DataTransform> transforms = new ArrayList<DataTransform>(columns.size());

        for(Column column : columns) {
            String javaTypeName = column.getJavaTypeName();

            if(javaTypeName.equalsIgnoreCase("boolean") || javaTypeName.equalsIgnoreCase("Boolean")
                    || javaTypeName.equalsIgnoreCase("byte") || javaTypeName.equalsIgnoreCase("Byte")
                    || javaTypeName.equalsIgnoreCase("short") || javaTypeName.equalsIgnoreCase("Short")
                    || javaTypeName.equalsIgnoreCase("int") || javaTypeName.equalsIgnoreCase("Integer")
                    || javaTypeName.equalsIgnoreCase("float") || javaTypeName.equalsIgnoreCase("Float")
                    || javaTypeName.equalsIgnoreCase("double") || javaTypeName.equalsIgnoreCase("Double")
                    || javaTypeName.equalsIgnoreCase("long") || javaTypeName.equalsIgnoreCase("Long")
            ) {
                transforms.add(new NumberTransform());
            }
            else if(javaTypeName.equalsIgnoreCase("String")) {
                transforms.add(new StringTransform());
            }
            else if(javaTypeName.equalsIgnoreCase("java.util.Date")) {
                transforms.add(new DateTransform());
            }
            else {
                transforms.add(new StringTransform());
            }
        }
        return transforms;
    }

    /**
     * @param columns
     * @return String
     */
    public String getPreparedSql(List<Column> columns) {
        StringBuilder buffer = new StringBuilder();
        buffer.append("INSERT INTO ");
        buffer.append(this.getTableName());
        buffer.append("(");

        int i = 0;
        int size = columns.size() - 1;
        Column column = null;

        for(i = 0; i < size; i++) {
            column = columns.get(i);
            buffer.append(column.getColumnName());
            buffer.append(", ");
        }

        column = columns.get(i);
        buffer.append(column.getColumnName());
        buffer.append(") values (");

        for(i = 0; i < size; i++) {
            column = columns.get(i);
            buffer.append("?, ");
        }

        column = columns.get(i);
        buffer.append("?)");

        return buffer.toString();
    }

    /**
     * @return the tableName
     */
    public String getTableName() {
        return this.tableName;
    }

    /**
     * @param tableName the tableName to set
     */
    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    /**
     * @return the connection
     */
    public Connection getConnection() {
        return this.connection;
    }

    /**
     * @param connection the connection to set
     */
    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    /**
     * @return the statement
     */
    public PreparedStatement getStatement() {
        return this.statement;
    }

    /**
     * @param statement the statement to set
     */
    public void setStatement(PreparedStatement statement) {
        this.statement = statement;
    }

    /**
     * @param connection
     */
    public void close(Connection connection) {
        if(connection != null) {
            try {
                if(!connection.isClosed()) {
                    connection.close();
                }
            }
            catch(SQLException e) {
            }
        }
    }

    /**
     * @param statement
     */
    public void close(Statement statement) {
        if(statement != null) {
            try {
                statement.close();
            }
            catch(SQLException e) {
            }
        }
    }

    /**
     * @param resultSet
     */
    public void close(ResultSet resultSet) {
        if(resultSet != null) {
            try {
                resultSet.close();
            }
            catch(SQLException e) {
            }
        }
    }
}
